package compiler;

import java.util.ArrayList;
import java.util.List;

public class Lexer {

    //解析第几行几列
    int line;
    int column;

    int index;
    int len;
    public static char[] src;
    int[] opTable = new int[256];
    int[] numTable = new int[256];

    public Lexer(char[] src) {
        Lexer.src = src;
        this.len = src.length;
        this.index = 0;
        this.line = 1;
        this.column = 0;

        opTable['+'] = 1;
        opTable['-'] = 1;
        opTable['*'] = 1;
        opTable['/'] = 1;
        opTable['%'] = 1;
        opTable['>'] = 1;
        opTable['<'] = 1;
        opTable['='] = 1;
        opTable['!'] = 1;
        opTable['~'] = 1;
        opTable['^'] = 1;
        opTable['('] = 1;
        opTable[')'] = 1;
        opTable['['] = 1;
        opTable[']'] = 1;
        opTable['{'] = 1;
        opTable['}'] = 1;
        opTable['.'] = 1;
        opTable[','] = 1;
        opTable['|'] = 1;
        opTable['&'] = 1;
        opTable[':'] = 1;
        opTable['@'] = 1;
        numTable['0'] = 1;
        numTable['1'] = 1;
        numTable['2'] = 1;
        numTable['3'] = 1;
        numTable['4'] = 1;
        numTable['5'] = 1;
        numTable['6'] = 1;
        numTable['7'] = 1;
        numTable['8'] = 1;
        numTable['9'] = 1;
    }


    public List<Token> lex() {
        List<Token> tokenList = new ArrayList<>();

        Token tk;
        while ((tk = nextToken()).type != TokenKind.TOKEN_EOF) {
            tokenList.add(tk);
        }
        return tokenList;
    }


    //下一个token
    public Token nextToken() {
        StringBuilder tokenVal = new StringBuilder();

        while (true) {
            char curChar = nextNoBlank();
            column++;
            switch (curChar) {
                case 0:
                    return new Token(TokenKind.TOKEN_EOF, "", line, column);
                case ';':
                    return new Token(TokenKind.TOKEN_BREAK, ";", line, column);
                default:
                    break;
            }
            if (curChar == '"') {
                curChar = nextChar();
                while (curChar != '"' && hasNextChar()) {
                    tokenVal.append(curChar);
                    curChar = nextChar();
                }
                return new Token(TokenKind.TOKEN_STR, tokenVal.toString(), line, column);
            }
            if (isNumber(curChar)) {
                tokenVal.append(curChar);
                while (hasNextChar() && Character.isDigit(curChar = nextChar()) || curChar == '.') {
                    tokenVal.append(curChar);
                }
                //TODO 小数点后没小数了要报错 1.
                backChar();
                return new Token(TokenKind.TOKEN_INT, tokenVal.toString(), line, column);
            }

            if (isOp(curChar)) {
                if (curChar == '+') {
                    char next;
                    if (hasNextChar() && '+' == (next = nextChar())) {
                        tokenVal.append(curChar).append(next);
                        return new Token(TokenKind.TOKEN_OP, Parser.opMap.get(tokenVal), tokenVal.toString(), line, column);
                    } else {
                        backChar();
                    }
                } else if (curChar == '=') {
                    char next;
                    if (hasNextChar() && '=' == (next = nextChar())) {
                        tokenVal.append(curChar).append(next);
                        return new Token(TokenKind.TOKEN_OP, Parser.opMap.get(tokenVal), tokenVal.toString(), line, column);
                    } else {
                        backChar();
                    }
                }
                tokenVal.append(curChar);
                return new Token(TokenKind.TOKEN_OP, Parser.opMap.get(curChar + ""), tokenVal.toString(), line, column);
            } else {
                tokenVal.append(curChar);
                while (hasNextChar() && Character.isLetterOrDigit(curChar = nextChar())) {
                    tokenVal.append(curChar);
                }
                backChar();
                if ("return".equals(tokenVal)) {
                    return new Token(TokenKind.TOKEN_OP, tokenVal.toString(), line, column);
                }
                return new Token(TokenKind.TOKEN_ID, tokenVal.toString(), line, column);
            }
        }
    }

    private boolean isNumber(char c) {
        return numTable[c] == 1;
    }

    private boolean isLetter(char c) {
        if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))
            return true;
        return false;
    }

    private boolean isOp(char c) {
        return opTable[c] == 1;
    }

    private void backChar() {
        index--;
    }


    public char nextChar() {
        if (index >= len) {
            return 0;
        }
        return src[index++];
    }

    public char nextNoBlank() {
        char c = nextChar();
        switch (c) {
            case ' ':
            case '\t':
            case '\r':
                index++;
                return nextNoBlank();
            case '\n':
                line++;
                column = 0;
                return nextNoBlank();
            default:
                return c;
        }
    }

    public boolean hasNextChar() {
        return index < len;
    }

}